package com.anttribe.components.infra.lang.reflect.scanner;

import com.anttribe.components.infra.lang.reflect.scanner.exception.ClassScannerException;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

/**
 * @author zhaoyong
 * @date 2021/11/4 0004
 */
@Slf4j
@Setter
public class FileClassScanner implements ClassScanner {

    /**
     * DEFAULT_CLASSPATH
     */
    private static final String DEFAULT_CLASSPATH = FileClassScanner.class.getClassLoader().getResource("").getPath();

    /**
     * 类加载路径
     */
    private String classpath = DEFAULT_CLASSPATH;

    /**
     * 扫描后的类文件
     */
    private Set<Class<?>> scanClasses = new HashSet<>();

    @Override
    public Set<Class<?>> scan(String basePackage, Predicate<Class<?>> predicate) {
        String scanFilePath = classpath + basePackage.replace(PACKAGE_SEPARATOR, File.separator);
        return doScan(new File(scanFilePath), basePackage, Boolean.TRUE, predicate);
    }

    /**
     * 扫描类文件
     *
     * @param classFile   类文件
     * @param packageName 包名
     * @param flag        类文件即包
     * @param predicate   筛选条件
     * @return Set<Class < ?>>
     */
    private Set<Class<?>> doScan(File classFile, String packageName, boolean flag, Predicate<Class<?>> predicate) {
        if (!classFile.exists()) {
            return null;
        }

        if (classFile.isDirectory()) {
            // 目录，递归扫描
            File[] childClassFiles = classFile.listFiles();
            if (!ArrayUtils.isEmpty(childClassFiles)) {
                if (!flag) {
                    packageName = packageName + PACKAGE_SEPARATOR + classFile.getName();
                }
                for (File childClassFile : childClassFiles) {
                    doScan(childClassFile, packageName, Boolean.FALSE, predicate);
                }
            }
        } else {
            // 如果是class文件，则处理
            if (StringUtils.endsWith(classFile.getName(), CLASS_SUFFIX)) {
                String className = packageName + PACKAGE_SEPARATOR + classFile.getName().substring(0, classFile.getName().lastIndexOf(PACKAGE_SEPARATOR));
                try {
                    Class<?> scanClass = Class.forName(className);
                    if (null != predicate && predicate.test(scanClass)) {
                        scanClasses.add(scanClass);
                    }
                } catch (ClassNotFoundException e) {
                    throw new ClassScannerException(e);
                }
            }
        }
        return scanClasses;
    }

}
